#ifndef ______Vector2_h___
#define ______Vector2_h___

//#include "MyCommonPrereq.h"
#include "Maths.h"

namespace MyCommon
{
	/** Standard 2-dimensional vector.
	@remarks
		A direction in 2D space represented as distances along the 2
		orthogonal axes (x, y). Note that positions, directions and
		scaling factors can be represented by a vector, depending on how
		you interpret the values.
	*/
	class Vector2
	{
	public:
		Real x,y;

		inline Vector2() : x(0),y(0) {}
		inline Vector2(const Real rx,const Real ry) : x(rx),y(ry) {}
		inline Vector2(const Real scale) : x(scale),y(scale) {}
		inline explicit Vector2( const Real coor[2] ): x(coor[0]),y(coor[1]){}
		inline explicit Vector2( const int coor[2] )
		{	x = (Real)coor[0]; y = (Real)coor[1]; }
		inline explicit Vector2( Real* const r ): x( r[0] ), y( r[1] ){}
		inline Vector2( const Vector2& v ): x(v.x),y(v.y){}

		inline Real operator [] ( const size_t i ) const
		{	assert( i < 2 );	return *(&x+i);	}
		inline Real& operator [] ( const size_t i )
		{	assert( i < 2 );	return *(&x+i);	}

		/// Pointer accessor for direct copying
		inline Real* ptr()	{	return &x;	}
		/// Pointer accessor for direct copying
		inline const Real* ptr() const	{	return &x;	}

		inline Vector2& operator = ( const Vector2& rkVector )
		{
			x = rkVector.x;
			y = rkVector.y;
			return *this;
		}

		inline Vector2& operator = ( const Real fScalar)
		{
			x = fScalar;
			y = fScalar;
			return *this;
		}

		inline bool operator == ( const Vector2& rkVector ) const
		{	return ( x == rkVector.x && y == rkVector.y );	}

		inline bool operator != ( const Vector2& rkVector ) const
		{	return ( x != rkVector.x || y != rkVector.y  );	}

		inline Vector2 operator + ( const Vector2& rkVector ) const
		{	return Vector2(x + rkVector.x,y + rkVector.y);	}

		inline Vector2 operator - ( const Vector2& rkVector ) const
		{	return Vector2(x - rkVector.x,y - rkVector.y);	}

		inline Vector2 operator * ( const Real fScalar ) const
		{	return Vector2(x * fScalar,y * fScalar);	}

		inline Vector2 operator * ( const Vector2& rhs) const
		{	return Vector2(x * rhs.x,y * rhs.y);	}

		inline Vector2 operator / ( const Real fScalar ) const
		{
			assert( fScalar != 0.0 && "Vector2::operator/ : trying to dovode by zero");
			Real fInv = Real(1.0) / fScalar;
			return Vector2(x * fInv,y * fInv);
		}

		inline Vector2 operator / ( const Vector2& rhs) const
		{	return Vector2(x / rhs.x,y / rhs.y);	}

		inline const Vector2& operator + () const
		{	return *this;	}

		inline Vector2 operator - () const
		{	return Vector2(-x, -y);	}

		inline friend Vector2 operator * ( const Real fScalar, const Vector2& rkVector )
		{	return Vector2(fScalar * rkVector.x,fScalar * rkVector.y);	}

		inline friend Vector2 operator / ( const Real fScalar, const Vector2& rkVector )
		{	return Vector2(fScalar / rkVector.x,fScalar / rkVector.y);	}

		inline friend Vector2 operator + (const Vector2& lhs, const Real rhs)
		{	return Vector2(lhs.x + rhs,lhs.y + rhs);	}

		inline friend Vector2 operator + (const Real lhs, const Vector2& rhs)
		{	return Vector2(lhs + rhs.x,lhs + rhs.y);	}

		inline friend Vector2 operator - (const Vector2& lhs, const Real rhs)
		{	return Vector2(lhs.x - rhs,lhs.y - rhs);	}

		inline friend Vector2 operator - (const Real lhs, const Vector2& rhs)
		{	return Vector2(lhs - rhs.x,lhs - rhs.y);	}

		inline Vector2& operator += ( const Vector2& v )
		{	x += v.x;	y += v.y;	return *this;	}

		inline Vector2& operator += ( const Real fScaler )
		{	x += fScaler;	y += fScaler;	return *this;	}

		inline Vector2& operator -= ( const Vector2& rkVector )
		{	x -= rkVector.x;	y -= rkVector.y;	return *this;	}

		inline Vector2& operator -= ( const Real fScaler )
		{	x -= fScaler;	y -= fScaler;	return *this;	}

		inline Vector2& operator *= ( const Real fScalar )
		{	x *= fScalar;	y *= fScalar;	return *this;	}

		inline Vector2& operator *= ( const Vector2& rkVector )
		{	x *= rkVector.x;	y *= rkVector.y;	return *this;	}

		inline Vector2& operator /= ( const Real fScalar )
		{
			assert( fScalar != 0.0 && "Vector2::operator/= : trying to divide by zero");
			Real fInv = Real(1.0) / fScalar;
			x *= fInv;
			y *= fInv;
			return *this;
		}

		inline Vector2& operator /= ( const Vector2& rkVector )
		{	x /= rkVector.x;	y /= rkVector.y;	return *this;	}

		inline Real distance(const Vector2& v)
		{
			Real dx = x-v.x;
			Real dy = y-v.y;
			return sqrt(dx*dx+dy*dy);
		}
		inline Real squaredDistance(const Vector2& v)
		{
			Real dx = x-v.x;
			Real dy = y-v.y;
			return (dx*dx+dy*dy);
		}

		/** Returns the length (magnitude) of the vector.
		@warning
			This operation requires a square root and is expensive in
			terms of CPU operations. If you don't need to know the exact
			length (e.g. for just comparing lengths) use squaredLength()
			instead.
		*/
		inline Real length () const {return Math::Sqrt( x * x + y * y );}

		/** Returns the square of the length(magnitude) of the vector.
		@remarks
			This  method is for efficiency - calculating the actual
			length of a vector requires a square root, which is expensive
			in terms of the operations required. This method returns the
			square of the length of the vector, i.e. the same as the
			length but before the square root is taken. Use this if you
			want to find the longest / shortest vector without incurring
			the square root.
		*/
		inline Real squaredLength () const {return x * x + y * y;}

		/** Calculates the dot (scalar) product of this vector with another.
		@remarks
			The dot product can be used to calculate the angle between 2
			vectors. If both are unit vectors, the dot product is the
			cosine of the angle; otherwise the dot product must be
			divided by the product of the lengths of both vectors to get
			the cosine of the angle. This result can further be used to
			calculate the distance of a point from a plane.
		@param
			vec Vector with which to calculate the dot product (together
			with this one).
		@returns
			A float representing the dot product value.
		*/
		inline Real dotProduct(const Vector2& vec) const
		{	return x * vec.x + y * vec.y;	}

		/** Normalizes the vector.
		@remarks
			This method normalizes the vector such that it's
			length / magnitude is 1. The result is called a unit vector.
		@note
			This function will not crash for zero-sized vectors, but there
			will be no changes made to their components.
		@returns The previous length of the vector.
		*/
		inline Real normalize()
		{
			Real fLength = Math::Sqrt( x * x + y * y);

			// Will also work for zero-sized vectors, but will change nothing
			if ( fLength > 1e-08 )
			{
				Real fInvLength = Real(1.0) / fLength;
				x *= fInvLength;
				y *= fInvLength;
			}
			return fLength;
		}

		/** Returns a vector at a point half way between this and the passed
			in vector.
		*/
		inline Vector2 midPoint( const Vector2& vec ) const
		{	return Vector2(( x + vec.x ) * Real(0.5),( y + vec.y ) * Real(0.5) );	}

		/** Returns true if the vector's scalar components are all greater
			that the ones of the vector it is compared against.
		*/
		inline bool operator < ( const Vector2& rhs ) const
		{
			if( x < rhs.x && y < rhs.y )
				return true;
			return false;
		}

		/** Returns true if the vector's scalar components are all smaller
			that the ones of the vector it is compared against.
		*/
		inline bool operator > ( const Vector2& rhs ) const
		{
			if( x > rhs.x && y > rhs.y )
				return true;
			return false;
		}

		/** Sets this vector's components to the minimum of its own and the
			ones of the passed in vector.
		@remarks
			'Minimum' in this case means the combination of the lowest
			value of x, y and z from both vectors. Lowest is taken just
			numerically, not magnitude, so -1 < 0.
		*/
		inline void makeFloor( const Vector2& cmp )
		{
			if( cmp.x < x ) x = cmp.x;
			if( cmp.y < y ) y = cmp.y;
		}

		/** Sets this vector's components to the maximum of its own and the
			ones of the passed in vector.
		@remarks
			'Maximum' in this case means the combination of the highest
			value of x, y and z from both vectors. Highest is taken just
			numerically, not magnitude, so 1 > -3.
		*/
		inline void makeCeil( const Vector2& cmp )
		{
			if( cmp.x > x ) x = cmp.x;
			if( cmp.y > y ) y = cmp.y;
		}

		/** Generates a vector perpendicular to this vector (eg an 'up' vector).
		@remarks
			This method will return a vector which is perpendicular to this
			vector. There are an infinite number of possibilities but this
			method will guarantee to generate one of them. If you need more
			control you should use the Quaternion class.
		*/
		inline Vector2 perpendicular(void) const
		{	return Vector2 (-y, x);	}

		/** Calculates the 2 dimensional cross-product of 2 vectors, which results
			in a single floating point value which is 2 times the area of the triangle.
		*/
		inline Real crossProduct( const Vector2& rkVector ) const
		{	return x * rkVector.y - y * rkVector.x;	}

		/** Generates a new random vector which deviates from this vector by a
			given angle in a random direction.
		@remarks
			This method assumes that the random number generator has already
			been seeded appropriately.
		@param
			angle The angle at which to deviate in radians
		@param
			up Any vector perpendicular to this one (which could generated
			by cross-product of this vector and any other non-collinear
			vector). If you choose not to provide this the function will
			derive one on it's own, however if you provide one yourself the
			function will be faster (this allows you to reuse up vectors if
			you call this method more than once)
		@returns
			A random vector which deviates from this vector by angle. This
			vector will not be normalized, normalise it if you wish
			afterwards.
		*/
		inline Vector2 randomDeviant(Real angle) const
		{
			angle *=  Math::UnitRandom() * Math::TWO_PI;
			Real cosa = cos(angle);
			Real sina = sin(angle);
			return  Vector2(cosa * x - sina * y,sina * x + cosa * y);
		}

		/** Returns true if this vector is zero length. */
		inline bool isZeroLength(void) const
		{
			Real sqlen = (x * x) + (y * y);
			return (sqlen < (1e-06 * 1e-06));
		}

		/** As normalise, except that this vector is unaffected and the
			normalized vector is returned as a copy. 
		*/
		inline Vector2 normalisedCopy(void) const
		{
			Vector2 ret = *this;
			ret.normalize();
			return ret;
		}

		/**	returns positive if v2 is clockwise of this vector,
			negative if anticlockwise (assuming the Y axis is pointing down,
			X axis to right like a Window app)
		*/
		enum{clockwise=1,anticlockwise=-1};
		inline int sign(const Vector2& v) const
		{
			if(y*v.x+x*v.y)
				return anticlockwise;
			else
				return clockwise;
		}

		/** Calculates a reflection vector to the plane with the given normal .
		@remarks 
			NB assumes 'this' is pointing AWAY FROM the plane, invert if it is not.
		*/
		inline Vector2 reflect(const Vector2& normal) const
		{	return Vector2( *this - ( 2 * this->dotProduct(normal) * normal ) );	}

		/** truncates a vector so that its length does not exceed max
		*/
		Real truncate(Real max)
		{
			Real len = length();
			if(len>max)
			{
				this->normalize();
				*this *= max;
				return max;
			}
			return len;
		}
		

		// special points
		static const Vector2 ZERO;
		static const Vector2 UNIT_X;
		static const Vector2 UNIT_Y;
		static const Vector2 NEGATIVE_UNIT_X;
		static const Vector2 NEGATIVE_UNIT_Y;
		static const Vector2 UNIT_SCALE;

		/** Function for writing to a stream.
		*/
		inline friend std::ostream& operator << ( std::ostream& o, const Vector2& v )
		{
			o << "Vector2(" << v.x << ", " << v.y <<  ")";
			return o;
		}

	};
}


#endif